package com.hp.demo.vteam.base;

import java.util.regex.Pattern;
import java.util.regex.Matcher;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.hp.demo.vteam.base.annotation.Exclude;
import com.hp.demo.vteam.base.annotation.PropertyExpr;
import com.hp.demo.vteam.base.annotation.PropertyRender;
public class ReflectUtils {
	public static final String ON_PARSE_ON_RLT = "sys_error";
	public static final String DEFAULT_EMPTY = "";
	public static interface LoopHandler {
		/**
		 * 一个节点开始
		 */
		void begin();

		/**
		 * 一个节点结束
		 */
		void end();
        
		/**
		 * 一个节点的子内容处理完成,并马上将处理下个节点
		 * 在JsonUtil.JsonParseUtils这个实现中,用来给json数据的各个属性打逗号..
		 */
		void onReadPropertySep();
       
		/**
		 * 准备开始处理一个属性
		 * @param object 开始处理时的值
		 * @param property 属性名
		 */
		void onReadPropertyBegin(Object object, String property);
		
		/**
		 * 处理一个属性完成
		 * @param object 处理完成后的属性值,即:读取PropertyExpr注解后处理后的值
		 * @param property 属性名
		 */
		void onReadPropertyEnd(Object object, String property);
	}

	/**
	 * 
	 * @param attr
	 * @return
	 */
	public static boolean basicType(Object attr) {
		return attr.getClass().isPrimitive() || attr instanceof String
				|| attr instanceof Integer || attr instanceof Long
				|| attr instanceof Float || attr instanceof Double;
	}

	public static String getter2Attr(String getter) {
		String attrTemp = getter.substring(3);
		attrTemp = (char) (attrTemp.charAt(0) + 32) + attrTemp.substring(1);
		return attrTemp;
	}

	public static String attr2Getter(String attr) {
		return "get" + (char) (attr.charAt(0) - 32) + attr.substring(1);
	}

	public static Object invokeGetter(Object obj, String methodName)
			throws SecurityException, NoSuchMethodException,
			IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		Class<?> clz = obj.getClass();
		Method method = clz.getMethod(methodName);
		return method.invoke(obj);
	}

	public static void clzBeanAttrLoop(Object object, LoopHandler handler) {
		handler.begin();
		Class<?> clz = object.getClass();
		Method[] methods = clz.getMethods();
		int methodCounts = methods.length;
		boolean hasPrev = false;
		for (int idx = 0; idx < methodCounts; idx++) {
			Method method = methods[idx];
			String methodName = method.getName();
			if (methodName.matches("get[A-Z][\\w]+")) {
				String attrName = getter2Attr(methodName);
				Field attr = null;
				try
				{
					attr = clz.getDeclaredField(attrName);
				}
				catch(Exception e)
				{
					//e.printStackTrace();
					continue;
				}

				boolean isExclude = attr.getAnnotation(Exclude.class) != null;
				if (isExclude) {
					continue;
				} else {
					if (hasPrev) {
						handler.onReadPropertySep();
					}
					handler.onReadPropertyBegin(object, attrName);
				}

				//
				Object currentMethodRlt = null;
				try
				{
					currentMethodRlt = method.invoke(object);
				}
				catch(Exception e)
				{
					e.printStackTrace();
					handler.onReadPropertyEnd(ON_PARSE_ON_RLT, attrName);
				}
				PropertyExpr propertyAnno = attr
						.getAnnotation(PropertyExpr.class);
				if (null != propertyAnno) {
					String[] subProperties = propertyAnno.property();

					final int subPropertisCount = subProperties.length;
					if (subPropertisCount == 1) {
						propertyReadIn(subProperties[0], propertyAnno,
								attrName, attr, currentMethodRlt, handler);
					} else {
						handler.begin();

						for (int proper_idx = 0; proper_idx < subPropertisCount; proper_idx++) {
							String subProperty = subProperties[proper_idx];
							handler.onReadPropertyBegin(currentMethodRlt,
									getPropertyAlias(subProperty));
							propertyReadIn(subProperty, propertyAnno, attrName,
									attr, currentMethodRlt, handler);
							if (proper_idx != subPropertisCount - 1) {
								handler.onReadPropertySep();
							}
						}
						handler.end();
					}

				} else {
					propertyReadIn(attrName, attr, currentMethodRlt, handler);
				}

				hasPrev = true;
			}
		}
		handler.end();
	}

	private static final Pattern EXPR_PATTERN = Pattern
			.compile("^([\\w]*)\\.(.*)$");

	private static String getPropertyAlias(String property) {
		return property.replaceAll("^.*\\.", "");
	}

	private static Object getSubAttr(Object obj, String attrExpr)
			throws SecurityException, IllegalArgumentException,
			NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		if (null == obj) {
			return null;
		}
		Matcher macher = EXPR_PATTERN.matcher(attrExpr);
		if (macher.find()) {
			String current = macher.group(1);
			String subs = macher.group(2);
			return getSubAttr(invokeGetter(obj, attr2Getter(current)), subs);
		} else {
			return invokeGetter(obj, attr2Getter(attrExpr));
		}
	}

	private static void propertyReadIn(String subProperty,
			PropertyExpr propertyAnno, String attrName, Field attr,
			Object currentMethodRlt, LoopHandler handler){
		try{
			currentMethodRlt = getSubAttr(currentMethodRlt, subProperty);
			if (currentMethodRlt == null) {
				currentMethodRlt = propertyAnno.empty();
			}
			propertyReadIn(attrName, attr, currentMethodRlt, handler);
		}
		catch(Exception e)
		{
			propertyReadIn(attrName, attr, ON_PARSE_ON_RLT, handler);
		}
	}

	private static void propertyReadIn(String attrName, Field attr,
			Object currentMethodRlt, LoopHandler handler) {
		if (currentMethodRlt == null) {
			handler.onReadPropertyEnd(DEFAULT_EMPTY, attrName);
			return;
		}
		PropertyRender renderAnno = attr.getAnnotation(PropertyRender.class);
		if (null != renderAnno) {
			currentMethodRlt = renderAnno.parser().render(currentMethodRlt);
		}

		if (basicType(currentMethodRlt)) {
			handler.onReadPropertyEnd(currentMethodRlt, attrName);
		} else {
			clzBeanAttrLoop(currentMethodRlt, handler);
		}
	}
}
